package edu.washington.cs.workflow.analysis.guistructure.uievents;

import java.util.Arrays;
import java.util.Collection;
import java.util.LinkedHashSet;

import javax.swing.text.JTextComponent;

import org.uispec4j.Button;
import org.uispec4j.CheckBox;
import org.uispec4j.ComboBox;
import org.uispec4j.ListBox;
import org.uispec4j.MenuItem;
import org.uispec4j.Panel;
import org.uispec4j.PasswordField;
import org.uispec4j.RadioButton;
import org.uispec4j.Slider;
import org.uispec4j.Spinner;
import org.uispec4j.TabGroup;
import org.uispec4j.Table;
import org.uispec4j.TextBox;
import org.uispec4j.ToggleButton;
import org.uispec4j.Tree;
import org.uispec4j.UIComponent;

import edu.washington.cs.workflow.analysis.guistructure.UIPos;
import edu.washington.cs.workflow.analysis.guistructure.uievents.UIEvent.UIAction;
import edu.washington.cs.workflow.util.Utils;

public class UIEventFactory {
	
	public static UIEvent replicateExecutedEvent(UIEvent oldEvent, UIComponent newUIC, Panel newPanel) {
		Utils.checkNotNull(oldEvent.getWindowFlag());
		UIEvent newEvent = replicateEventInternal(oldEvent, newUIC, newPanel);
		newEvent.setWindowFlag(oldEvent.getWindowFlag());
		return newEvent;
	}
	
	private static UIEvent replicateEventInternal(UIEvent oldEvent, UIComponent newUIC, Panel newPanel) {
		Utils.checkNotNull(newPanel);
		UIComponent oldUIC = oldEvent.getUIComponent();
		Utils.checkNotNull(oldUIC);
		Utils.checkNotNull(newUIC);
		Utils.checkTrue(oldUIC.getAwtComponent().getClass().equals(newUIC.getAwtComponent().getClass())
				, "Can not replicate event from: " + oldUIC.getAwtComponent().getClass()
				+ " type to: " + newUIC.getAwtComponent().getClass());
		if(oldUIC instanceof Button) {
			return makeButtonEvent((Button)newUIC, newPanel, oldEvent.getAction());
		} else if (oldUIC instanceof TextBox) {
			if(oldUIC.getAwtComponent() instanceof JTextComponent) {
				Utils.checkTrue(newUIC.getAwtComponent() instanceof JTextComponent);
				TextBox oldTB = (TextBox)oldUIC;
				return makeTextComponentEvent((TextBox)newUIC, newPanel, oldEvent.getAction(), oldTB.getText());
			} else {
				return null;
			}
		} else if (oldUIC instanceof Table) {
//			Table oldT  = (Table)oldUIC;
			TableEvent oldTE = (TableEvent)oldEvent;
			return makeTableEvent((Table)newUIC, newPanel, oldEvent.getAction(), oldTE.clickX, oldTE.clickY);
		} else if (oldUIC instanceof Tree) {
			TreeEvent oldTree = (TreeEvent)oldEvent;
			return makeTreeEvent((Tree)newUIC, newPanel, oldEvent.getAction(), oldTree.getPath());
		} else if (oldUIC instanceof MenuItem) {
			return makeMenuItemEvent((MenuItem)newUIC, newPanel, oldEvent.getAction());
		} else if (oldUIC instanceof CheckBox) {
			return makeCheckBoxEvent((CheckBox)newUIC, newPanel, oldEvent.getAction());
		} else if (oldUIC instanceof ComboBox) {
			ComboBoxEvent oldComboBoxEvent = (ComboBoxEvent)oldEvent;
			return makeComboBoxEvent((ComboBox)newUIC, newPanel, oldEvent.getAction(), oldComboBoxEvent.getSelectedIndex());
		} else if (oldUIC instanceof ListBox) {
			ListBoxEvent oldListBoxEvent = (ListBoxEvent)oldEvent;
			return makeListBoxEvent((ListBox)newUIC, newPanel, oldEvent.getAction(), oldListBoxEvent.getSelectedIndex());
		} else if (oldUIC instanceof PasswordField) {
			PasswordFieldEvent oldPwdEvent = (PasswordFieldEvent)oldEvent;
			return makePasswordFieldEvent((PasswordField)newUIC, newPanel, oldEvent.getAction(), oldPwdEvent.getText()); 
		} else if (oldUIC instanceof RadioButton) {
			return makeRadioButtonEvent((RadioButton)newUIC, newPanel, oldEvent.getAction());
		} else if (oldUIC instanceof Slider) {
			SliderEvent oldSliderEvent = (SliderEvent)oldEvent;
			return makeSliderEvent((Slider)newUIC, newPanel, oldEvent.getAction(), oldSliderEvent.getPercentage());
		} else if (oldUIC instanceof Spinner) {
			return makeSpinnerEvent((Spinner)newUIC, newPanel, oldEvent.getAction());
		} else if (oldUIC instanceof TabGroup) {
			TabGroupEvent oldTabGroupEvent = (TabGroupEvent)oldEvent;
			return makeTabGroupEvent((TabGroup)newUIC, newPanel, oldEvent.getAction(), oldTabGroupEvent.getSelectedIndex());
		} else if (oldUIC instanceof ToggleButton) {
			return makeToggleButtonEvent((ToggleButton)newUIC, newPanel, oldEvent.getAction());
		}
		
		return null;
	}
	
	public static Collection<UIEvent> createAllEvents(UIComponent ui, Panel panel, UIPos pos) {
//		Collection<UIEvent> events = new LinkedHashSet<UIEvent>();
//		UIEvent e = createEvent(ui, panel, pos);
//		events.add(e);
//		return events;
		Collection<UIEvent> events = createAllEvents(ui, panel);
		for(UIEvent e : events) {
			e.setUIComponentPos(pos);
		}
		return events;
	}
	
	public static UIEvent createRandomEvent(UIComponent ui, Panel panel, UIPos pos) {
		Utils.checkNotNull(pos);
		UIEvent event = createRandomEvent(ui, panel);
		if(event != null) {
			event.setUIComponentPos(pos);
		}
		return event;
	}
	
	private static UIEvent createRandomEvent(UIComponent ui, Panel panel) {
		if(ui instanceof Button) {
			return makeButtonEvent((Button)ui, panel, UIAction.Click);
		} else if (ui instanceof TextBox) {
			if(ui.getAwtComponent() instanceof JTextComponent) {
			    return makeTextComponentEvent((TextBox)ui, panel, UIAction.SetText);
			} else {
				return null;
			}
		} else if (ui instanceof Table) {
			Table t = (Table)ui;
			if(t.getColumnCount() == 0 || t.getRowCount() == 0) {
//				System.out.println("empty table");
				return null;
			}
//			System.out.println("non-empty table");
			UIAction randomAction = Utils.randomElement(Arrays.asList(UIAction.Click, UIAction.RightClick, UIAction.DoubleClick));
			int x = Utils.nextRandomInt(t.getRowCount());
			int y = Utils.nextRandomInt(t.getColumnCount());
			return makeTableEvent(t, panel, randomAction, x, y);
		} else if (ui instanceof Tree) {
			Tree tree = (Tree)ui;
			UIAction randomAction = Utils.randomElement(Arrays.asList(UIAction.Click, UIAction.RightClick, UIAction.DoubleClick));
			String randomPath = TreeEvent.randomPath(tree);
			return makeTreeEvent(tree, panel, randomAction, randomPath);
		} else if (ui instanceof MenuItem) {
//			MenuItem item = (MenuItem)ui;
			return makeMenuItemEvent((MenuItem)ui, panel, UIAction.Click);
		} else if (ui instanceof CheckBox) {
			return makeCheckBoxEvent((CheckBox)ui, panel, UIAction.Click);
		} else if (ui instanceof ComboBox) {
			//XXX ignore set text now
			ComboBox comboBox = (ComboBox)ui;
			int selectedIndex = Utils.nextRandomInt(comboBox.getAwtComponent().getModel().getSize());
			return makeComboBoxEvent(comboBox, panel, UIAction.Click, selectedIndex);
		} else if (ui instanceof ListBox) {
			ListBox listBox = (ListBox)ui;
			int size = listBox.getAwtComponent().getModel().getSize();
			if(size == 0) {
				return null;
			}
			int selectedIndex = Utils.nextRandomInt(size);
			UIAction action = Utils.randomElement(Arrays.asList(UIAction.Click, UIAction.RightClick, UIAction.DoubleClick));
			return makeListBoxEvent(listBox, panel, action, selectedIndex);
		} else if (ui instanceof PasswordField) {
			return makePasswordFieldEvent((PasswordField)ui, panel, UIAction.SetText, Utils.nextRandomIntString(9));
		} else if (ui instanceof RadioButton) {
			return makeRadioButtonEvent((RadioButton)ui, panel, UIAction.Click);
		} else if (ui instanceof Slider) {
			return makeSliderEvent((Slider)ui, panel, UIAction.SetPost, Utils.nextRandomInt(101));
		} else if (ui instanceof Spinner) {
			//XXX ignore set text
			UIAction action = Utils.nextBoolean() ? UIAction.Nextvalue : UIAction.Prevalue;
			return makeSpinnerEvent((Spinner)ui, panel, action);
		} else if (ui instanceof TabGroup) {
			TabGroup tabGroup = (TabGroup)ui;
			if(tabGroup.getAwtComponent().getTabCount() == 0) {
				return null;
			}
			int selectedIndex = Utils.nextRandomInt(tabGroup.getAwtComponent().getTabCount());
			return makeTabGroupEvent(tabGroup, panel, UIAction.Click, selectedIndex);
		} else if (ui instanceof ToggleButton) {
			UIAction action = Utils.nextBoolean() ? UIAction.Select : UIAction.Unselect;
			return makeToggleButtonEvent((ToggleButton)ui, panel, action);
		}
		
		return null;
	}
	
	private static Collection<UIEvent> createAllEvents(UIComponent ui, Panel panel) {
		Collection<UIEvent> events = new LinkedHashSet<UIEvent>();
		//add all possible events
		if(ui instanceof Button) {
			UIEvent e = makeButtonEvent((Button)ui, panel, UIAction.Click);
			if(e != null) {
				events.add(e);
			}
		} else if (ui instanceof TextBox) {
			if(ui.getAwtComponent() instanceof JTextComponent) {
			    UIEvent e = makeTextComponentEvent((TextBox)ui, panel, UIAction.SetText, Utils.nextRandomIntString(9));
			    if(e != null) {
			    	events.add(e);
			    }
			}
		} else if (ui instanceof Table) {
			Table t = (Table)ui;
			if(!(t.getColumnCount() == 0 || t.getRowCount() == 0)) {
				UIAction a1 = UIAction.Click;
				UIAction a2 = UIAction.RightClick;
				UIAction a3 = UIAction.DoubleClick;
				UIAction a4 = UIAction.SetText;
			    int x = Utils.nextRandomInt(t.getRowCount());
			    int y = Utils.nextRandomInt(t.getColumnCount());
			    UIEvent e1 = makeTableEvent(t, panel, a1, x, y);
			    UIEvent e2 = makeTableEvent(t, panel, a2, x, y);
			    UIEvent e3 = makeTableEvent(t, panel, a3, x, y);
			    UIEvent e4 = makeTableEvent(t, panel, a4, x, y, Utils.nextRandomIntString(6));
			    if(e1 != null) {
			    	events.add(e1);
			    }
			    if(e2 != null) {
			    	events.add(e2);
			    }
			    if(e3 != null) {
			    	events.add(e3);
			    }
			    if(e4 != null) {
			    	events.add(e4);
			    }
			}
		} else if (ui instanceof Tree) {
			Tree tree = (Tree)ui;
//			UIAction randomAction = Utils.randomElement(Arrays.asList(UIAction.Click, UIAction.RightClick, UIAction.DoubleClick));
			UIAction a1 = UIAction.Click;
			UIAction a2 = UIAction.RightClick;
			UIAction a3 = UIAction.DoubleClick;
			String randomPath = TreeEvent.randomPath(tree);
			UIEvent e1 = makeTreeEvent(tree, panel, a1, randomPath);
			UIEvent e2 = makeTreeEvent(tree, panel, a2, randomPath);
			UIEvent e3 = makeTreeEvent(tree, panel, a3, randomPath);
			if(e1 != null) {
				events.add(e1);
			}
			if(e2 != null) {
				events.add(e2);
			}
			if(e3 != null) {
				events.add(e3);
			}
		} else if (ui instanceof MenuItem) {
//			MenuItem item = (MenuItem)ui;
			UIEvent e= makeMenuItemEvent((MenuItem)ui, panel, UIAction.Click);
			if(e != null) {
				events.add(e);
			}
		} else if (ui instanceof CheckBox) {
			UIEvent e = makeCheckBoxEvent((CheckBox)ui, panel, UIAction.Click);
			if(e != null) {
				events.add(e);
			}
		} else if (ui instanceof ComboBox) {
			//XXX ignore set text now
			ComboBox comboBox = (ComboBox)ui;
			int selectedIndex = Utils.nextRandomInt(comboBox.getAwtComponent().getModel().getSize());
			UIEvent e = makeComboBoxEvent(comboBox, panel, UIAction.Click, selectedIndex);
			if(e != null) {
				events.add(e);
			}
		} else if (ui instanceof ListBox) {
			ListBox listBox = (ListBox)ui;
			int sizeOfListBox = listBox.getAwtComponent().getModel().getSize();
			if(sizeOfListBox > 0) {
			    int selectedIndex = Utils.nextRandomInt(sizeOfListBox);
//			UIAction action = Utils.randomElement(Arrays.asList(UIAction.Click, UIAction.RightClick, UIAction.DoubleClick));
			    UIAction a1 = UIAction.Click;
			    UIAction a2 = UIAction.RightClick;
			    UIAction a3 = UIAction.DoubleClick;
			    UIEvent e1 = makeListBoxEvent(listBox, panel, a1, selectedIndex);
			    UIEvent e2 = makeListBoxEvent(listBox, panel, a2, selectedIndex);
			    UIEvent e3 = makeListBoxEvent(listBox, panel, a3, selectedIndex);
			    if(e1 != null) {
				    events.add(e1);
			    }
			    if(e2 != null) {
				    events.add(e2);
			    }
			    if(e3 != null) {
				    events.add(e3);
			    }
			}
		} else if (ui instanceof PasswordField) {
			UIEvent e = makePasswordFieldEvent((PasswordField)ui, panel, UIAction.SetText, Utils.nextRandomString());
			if(e != null) {
				events.add(e);
			}
		} else if (ui instanceof RadioButton) {
			UIEvent e = makeRadioButtonEvent((RadioButton)ui, panel, UIAction.Click);
			if(e != null) {
				events.add(e);
			}
		} else if (ui instanceof Slider) {
			UIEvent e = makeSliderEvent((Slider)ui, panel, UIAction.SetPost, Utils.nextRandomInt(101));
			if(e != null) {
				events.add(e);
			}
		} else if (ui instanceof Spinner) {
			//XXX ignore set text
//			UIAction action = Utils.nextBoolean() ? UIAction.Nextvalue : UIAction.Prevalue;
			UIAction a1 = UIAction.Nextvalue;
			UIAction a2 = UIAction.Prevalue;
			UIEvent e1 = makeSpinnerEvent((Spinner)ui, panel, a1);
			UIEvent e2 = makeSpinnerEvent((Spinner)ui, panel, a2);
			if(e1 != null) {
				events.add(e1);
			}
			if(e2 != null) {
				events.add(e2);
			}
		} else if (ui instanceof TabGroup) {
			TabGroup tabGroup = (TabGroup)ui;
			if(tabGroup.getAwtComponent().getTabCount() != 0) {
//			    int selectedIndex = Utils.nextRandomInt(tabGroup.getAwtComponent().getTabCount());
				for(int selectedIndex = 0; selectedIndex < tabGroup.getAwtComponent().getTabCount(); selectedIndex++ ) {
			        UIEvent e = makeTabGroupEvent(tabGroup, panel, UIAction.Click, selectedIndex);
			        if(e != null) {
			        	events.add(e);
			        }
				}
			}
		} else if (ui instanceof ToggleButton) {
//			UIAction action = Utils.nextBoolean() ? UIAction.Select : UIAction.Unselect;
			UIAction a1 = UIAction.Select;
			UIAction a2 = UIAction.Unselect;
			UIEvent e1 = makeToggleButtonEvent((ToggleButton)ui, panel, a1);
			UIEvent e2 = makeToggleButtonEvent((ToggleButton)ui, panel, a2);
			if(e1 != null) {
				events.add(e1);
			}
			if(e2 != null) {
				events.add(e2);
			}
		}
		
		return events;
	}

	public static ButtonEvent makeButtonEvent(Button b, Panel panel, UIAction action) {
		return new ButtonEvent(b, panel, action);
	}
	
	public static CheckBoxEvent makeCheckBoxEvent(CheckBox checkBox, Panel panel, UIAction action) {
		return new CheckBoxEvent(checkBox, panel, action);
	}
	
	public static ComboBoxEvent makeComboBoxEvent(ComboBox comboBox, Panel panel, UIAction action, int selectedIndex) {
		return new ComboBoxEvent(comboBox, panel, action, selectedIndex, null);
	}
	
//	public static ComboBoxEvent makeComboBoxEvent(ComboBox comboBox, Panel panel, UIAction action, String text) {
//		return new ComboBoxEvent(comboBox, panel, action, -1, text);
//	}
	
	public static ListBoxEvent makeListBoxEvent(ListBox listBox, Panel panel,
			UIAction action, int selectedIndex) {
		return new ListBoxEvent(listBox, panel, action, selectedIndex);
	}
	
	public static MenuItemEvent makeMenuItemEvent(MenuItem item, Panel panel, UIAction action) {
		return new MenuItemEvent(item, panel, action);
	}
	
	public static PasswordFieldEvent makePasswordFieldEvent(PasswordField field, Panel panel, UIAction action, String text) {
		return new PasswordFieldEvent(field, panel, action, text);
	}
	
	public static RadioButtonEvent makeRadioButtonEvent(RadioButton radio, Panel panel, UIAction action) {
		return new RadioButtonEvent(radio, panel, action);
	}
	
	public static SliderEvent makeSliderEvent(Slider slider, Panel panel, UIAction action, int percentage) {
		return new SliderEvent(slider, panel, action, percentage);
	}
	
	public static SpinnerEvent makeSpinnerEvent(Spinner spinner, Panel panel, UIAction action) {
		return new SpinnerEvent(spinner, panel, action, null);
	}
	
//	public static SpinnerEvent makeSpinnerEvent(Spinner spinner, Panel panel, UIAction action, String text) {
//		return new SpinnerEvent(spinner, panel, action, text);
//	}
	
	public static TabGroupEvent makeTabGroupEvent(TabGroup tabGroup, Panel panel,
			UIAction action, int selectedIndex) {
		if(selectedIndex >= tabGroup.getAwtComponent().getTabCount()) {
			return null;
		}
		return new TabGroupEvent(tabGroup, panel, action, selectedIndex);
	}
	
	public static TreeEvent makeTreeEvent(Tree tree, Panel panel, UIAction action, String path) {
		return new TreeEvent(tree, panel, action, path);
	}
	
	public static TableEvent makeTableEvent(Table table, Panel panel, UIAction action, int x, int y) {
		return new TableEvent(table, panel, action, x, y);
	}
	
	public static TableEvent makeTableEvent(Table table, Panel panel, UIAction action, int x, int y, String text) {
		if(!table.cellIsEditable(x, y).isTrue()) {
			return null;
		}
		return new TableEvent(table, panel, action, x, y, text);
	}
	
	public static TextComponentEvent makeTextComponentEvent(TextBox box, Panel p, UIAction action) {
		return new TextComponentEvent(box, p, action);
	}
	
	public static TextComponentEvent makeTextComponentEvent(TextBox box, Panel p, UIAction action, String text) {
		return new TextComponentEvent(box, p, action, text);
	}
	
	public static ToggleButtonEvent makeToggleButtonEvent(ToggleButton toggle, Panel panel, UIAction action) {
		return new ToggleButtonEvent(toggle, panel, action);
	}
}